import { Exception } from "../Util/Exception";
import { SeqData } from "./SeqData";
import { SerializeException } from "./SerializeException";
import ByteArray = require("bytearray-node");
// did
type int = number;
//class SerializeStream
const MAX_VALUE = 2 ** 32 - 1;
export class SerializeStream {
  constructor() {
    this.byteArray = new ByteArray();
  }
  public readByte(): int {
    var loc1 = this._byteArray.readByte();
    if (loc1 < 0) {
      return loc1 + 256;
    }
    return loc1;
  }

  public readByteSeq(): Array<int> {
    var ret = new Array<int>();
    var size: int = this.readSize();
    var i = 0;
    while (i < size) {
      ret.push(this.readByte());
      ++i;
    }
    return ret;
  }

  public writeByte(arg1: int): void {
    this._byteArray.writeByte(arg1);
    return;
  }

  public writeByteSeq(arg1: Array<int>): void {
    if (arg1 == null) {
      this.writeSize(0);
      return;
    }
    this.writeSize(arg1.length);
    var loc1 = 0;
    while (loc1 < arg1.length) {
      this.writeByte(arg1[loc1]);
      ++loc1;
    }
    return;
  }

  public writeBool(arg1: boolean): void {
    if (arg1) {
      this._byteArray.writeByte(1);
    } else {
      this._byteArray.writeByte(0);
    }
    return;
  }

  public writeBoolSeq(arg1: Array<boolean>): void {
    if (arg1 == null) {
      this.writeSize(0);
      return;
    }
    this.writeSize(arg1.length);
    var loc1 = 0;
    while (loc1 < arg1.length) {
      this.writeBool(arg1[loc1]);
      ++loc1;
    }
    return;
  }

  public readBool(): boolean {
    var loc1 = this._byteArray.readByte();
    return loc1 !== 0;
  }

  public readBoolSeq(): Array<any> {
    var loc1 = new Array<any>();
    var loc2 = this.readSize();
    var loc3 = 0;
    while (loc3 < loc2) {
      loc1.push(this.readBool());
      ++loc3;
    }
    return loc1;
  }

  public readSize(): int {
    var loc1 = this._byteArray.readUnsignedByte();
    if (loc1 == 255) {
      return this._byteArray.readInt();
    }
    return loc1;
  }

  public writeSize(arg1: int) {
    if (arg1 < 255) {
      this._byteArray.writeUnsignedByte(arg1);
    } else {
      this._byteArray.writeUnsignedByte(255);
      this._byteArray.writeInt(arg1);
    }
  }

  public readInt(): int {
    return this._byteArray.readInt();
  }

  public readIntSeq(): Array<int> {
    var loc1 = new Array<int>();
    var loc2 = this.readSize();
    var loc3 = 0;
    while (loc3 < loc2) {
      loc1.push(this.readInt());
      ++loc3;
    }
    return loc1;
  }

  public writeInt(arg1: int) {
    this._byteArray.writeInt(arg1);
    return;
  }

  public writeIntSeq(arg1: Array<int>): void {
    if (arg1 == null) {
      this.writeSize(0);
      return;
    }
    this.writeSize(arg1.length);
    var loc1 = 0;
    while (loc1 < arg1.length) {
      this.writeInt(arg1[loc1]);
      ++loc1;
    }
    return;
  }

  public readShort(): int {
    return this._byteArray.readShort();
  }

  public readShortSeq(): Array<int> {
    var loc1 = new Array<int>();
    var loc2 = this.readSize();
    var loc3 = 0;
    while (loc3 < loc2) {
      loc1.push(this.readShort());
      ++loc3;
    }
    return loc1;
  }

  public writeShort(arg1: int): void {
    this._byteArray.writeShort(arg1);
    return;
  }

  public writeShortSeq(arg1: Array<int>): void {
    if (arg1 == null) {
      this.writeSize(0);
      return;
    }
    this.writeSize(arg1.length);
    var loc1 = 0;
    while (loc1 < arg1.length) {
      this.writeShort(arg1[loc1]);
      ++loc1;
    }
    return;
  }

  public readLong(): int {
    var loc1 = this._byteArray.readInt();
    var loc2 = this._byteArray.readInt();

    return loc2 * (MAX_VALUE + 1) + loc1;
  }

  public readLongSeq(): Array<int> {
    var loc1 = new Array<int>();
    var loc2 = this.readSize();
    var loc3 = 0;
    while (loc3 < loc2) {
      loc1.push(this.readLong());
      ++loc3;
    }
    return loc1;
  }

  public writeLong(arg1: int): void {
    var loc1 = arg1 % (MAX_VALUE + 1);
    var loc2 = arg1 / (MAX_VALUE + 1);
    if (arg1 >= 0) {
      this._byteArray.writeUnsignedInt(loc1);
      this._byteArray.writeInt(loc2);
    } else if (loc2 != 0) {
      this._byteArray.writeUnsignedInt(loc1);
      this._byteArray.writeInt(loc2);
    } else {
      this._byteArray.writeUnsignedInt(arg1);
      this._byteArray.writeInt(-1);
    }
    return;
  }

  public writeLongSeq(arg1: Array<int>): void {
    if (arg1 == null) {
      this.writeSize(0);
      return;
    }
    this.writeSize(arg1.length);
    var loc1 = 0;
    while (loc1 < arg1.length) {
      this.writeLong(arg1[loc1]);
      ++loc1;
    }
    return;
  }

  public readString(): string {
    var loc1 = this.readSize();
    return this._byteArray.readUTFBytes(loc1);
  }

  public readStringSeq(): Array<string> {
    var loc1 = new Array<string>();
    var loc2 = this.readSize();
    var loc3 = 0;
    while (loc3 < loc2) {
      loc1.push(this.readString());
      ++loc3;
    }
    return loc1;
  }

  public writeString(arg1: string): void {
    if (null == arg1) {
      this.writeSize(0);
      return;
    }
    var loc1 = new ByteArray();
    loc1.writeUTFBytes(arg1);
    this.writeSize(loc1.length);
    this._byteArray.writeBytes(loc1);
    return;
  }

  public writeStringSeq(arg1: Array<string>): void {
    if (null == arg1) {
      this.writeSize(0);
      return;
    }
    this.writeSize(arg1.length);
    var loc1 = 0;
    while (loc1 < arg1.length) {
      this.writeString(arg1[loc1]);
      ++loc1;
    }
    return;
  }

  public readFloat(): number {
    return this._byteArray.readFloat();
  }

  public readFloatSeq(): Array<number> {
    var loc1 = new Array<number>();
    var loc2 = this.readSize();
    var loc3 = 0;
    while (loc3 < loc2) {
      loc1.push(this.readFloat());
      ++loc3;
    }
    return loc1;
  }

  public writeFloat(arg1: number): void {
    this._byteArray.writeFloat(arg1);
    return;
  }

  public writeFloatSeq(arg1: Array<number>): void {
    if (arg1 == null) {
      this.writeSize(0);
      return;
    }
    this.writeSize(arg1.length);
    var loc1 = 0;
    while (loc1 < arg1.length) {
      this.writeFloat(arg1[loc1]);
      ++loc1;
    }
    return;
  }

  public readDouble(): number {
    return this._byteArray.readDouble();
  }

  public readDoubleSeq(): Array<int> {
    var loc1 = new Array<int>();
    var loc2 = this.readSize();
    var loc3 = 0;
    while (loc3 < loc2) {
      loc1.push(this.readDouble());
      ++loc3;
    }
    return loc1;
  }

  public writeDouble(arg1: number): void {
    this._byteArray.writeDouble(arg1);
    return;
  }

  public writeDoubleSeq(arg1: Array<number>): void {
    if (arg1 == null) {
      this.writeSize(0);
      return;
    }
    this.writeSize(arg1.length);
    var loc1 = 0;
    while (loc1 < arg1.length) {
      this.writeDouble(arg1[loc1]);
      ++loc1;
    }
    return;
  }

  public readDate(): Date {
    return new Date(this.readInt() * 1000);
  }

  public readDateSeq(): Array<Date> {
    var loc1 = new Array<Date>();
    var loc2 = this.readSize();
    var loc3 = 0;
    while (loc3 < loc2) {
      loc1.push(this.readDate());
      ++loc3;
    }
    return loc1;
  }

  public writeDate(arg1: Date) {
    this._byteArray.writeInt(arg1.getTime() / 1000);
  }

  public writeDateSeq(arg1: Array<Date>): void {
    if (arg1 == null) {
      this.writeSize(0);
      return;
    }
    this.writeSize(arg1.length);
    var loc1 = 0;
    while (loc1 < arg1.length) {
      this.writeDate(arg1[loc1]);
      ++loc1;
    }
    return;
  }

  public writeByteArray(arg1: SerializeStream): void {
    this.byteArray.writeBytes(arg1.byteArray);
  }

  public writeException(arg1: Exception): void {
    this.writeString(arg1.message);
    this.writeInt(arg1.code);
  }

  public readException(arg1: Exception): void {
    arg1.message = this.readString();
    arg1.code = this.readInt();
    return;
  }

  public startSeq(numElements: int, minSize: int): void {
    if (numElements == 0) {
      return;
    }
    var seqDataStack = new SeqData(numElements, minSize);
    seqDataStack.previous = this._seqDataStack;
    this._seqDataStack = seqDataStack;
    var len_r = this.getBytesLeft();
    if (this._seqDataStack.previous != null) {
      this.checkSeqLen(len_r);
    } else if (numElements * minSize > len_r) {
      throw new SerializeException();
    }
    return;
  }

  public checkSeq(): void {
    this.checkSeqLen(this.getBytesLeft());
    return;
  }

  /**
   * 校验序列总长度是不是小于给定参数
   * @param total
   */
  public checkSeqLen(total: int): void {
    var i = 0;
    var seqDataStack = this._seqDataStack;
    do {
      i += (seqDataStack.numElements - 1) * seqDataStack.minSize;
      seqDataStack = seqDataStack.previous;
    } while (seqDataStack != null);
    if (i > total) {
      throw new SerializeException();
    }
    return;
  }

  /**
   * 完成一个元素处理
   */
  public endElement(): void {
    this._seqDataStack.numElements--;
    return;
  }
  /**
   * 已处理完一个序列
   * @param arg1
   */
  public endSeq(arg1: int): void {
    if (arg1 == 0) {
      return;
    }
    this._seqDataStack = this._seqDataStack.previous;
    return;
  }

  /**
   * 校验余下元素数量是否匹配固定数量
   * @param arg1
   * @param arg2
   */
  public checkFixedSeq(numElements: int, minSize: int): void {
    var len_r = this.getBytesLeft();
    if (this._seqDataStack != null) {
      this.checkSeqLen(len_r - numElements * minSize);
    } else if (numElements * minSize > len_r) {
      throw new SerializeException();
    }
    return;
  }

  public getBytesLeft(): int {
    return this.byteArray.length - this.byteArray.position;
  }

  public clear(): void {
    this.byteArray = new ByteArray();
    return;
  }

  public set byteArray(arg1: ByteArray) {
    this._byteArray = arg1;
    this._byteArray.endian = "LE";
    // this._byteArray.endian = flash.utils.Endian.LITTLE_ENDIAN;
    return;
  }

  public get byteArray(): ByteArray {
    return this._byteArray;
  }

  private _byteArray!: ByteArray;
  // @ts-ignore
  private _seqDataStack!: SeqData = null;
}
